package com.huang.qqslidemenu;

import android.content.Context;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;

import com.huang.qqslidemenu.utils.ColorUtil;
import com.nineoldandroids.animation.ArgbEvaluator;
import com.nineoldandroids.animation.FloatEvaluator;
import com.nineoldandroids.animation.IntEvaluator;
import com.nineoldandroids.view.ViewHelper;

/**
 * 侧拉菜单控件
 *
 * Created by huangRZ on 2016/9/14.
 */
public class SlideMenu extends FrameLayout {

    private View slideMenu;//侧拉菜单栏
    private View slideMain;//主页面
    private ViewDragHelper viewDragHelper; //处理View拖拽的解析类
    private int dragRange; //定义拖拽范围
    private FloatEvaluator floatEvaluator;
    private IntEvaluator intEvaluator;
    private ArgbEvaluator argbEvaluator;

    public SlideMenu(Context context) {
        super(context);
        init();
    }

    public SlideMenu(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public SlideMenu(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    /**
     * 用于初始化
     */
    private void init(){
        /** 1、创建一个拖拽View的解析类实例，专门用于View的拖拽处理 */
        viewDragHelper = ViewDragHelper.create(this, new MyCallback());

        /** 创建计算器 */
        floatEvaluator = new FloatEvaluator();
        intEvaluator = new IntEvaluator();
        //颜色计算器
        argbEvaluator = new ArgbEvaluator();

    }

    private DragState mCurrentState = DragState.Close; //当前状态
    /**
     * 定义状态常量
     */
    enum DragState{
        Open, Close;
    }

    /**
     * 获取当前状态
     */
    public DragState getDragState(){
        return mCurrentState;
    }

    /**
     * 该方法在onMeasure执行完之后执行，那么可以在该方法中初始化自己和子View的宽高
     */
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        dragRange = (int) (getMeasuredWidth() * 0.6f);
    }

    /**
     * 当DragLayout的xml布局的结束标签被读取完成会执行该方法，此时会知道自己有几个子View了
     * 一般用来初始化子View的引用
     */
    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();

        /** 简单的异常处理：只允许两个子View */
        if(getChildCount()!=2){
            throw new IllegalArgumentException("SlideMenu only have 2 children!");
        }

        slideMenu = getChildAt(0);//侧拉菜单栏
        slideMain = getChildAt(1);//主页面
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        /** 2、处理触摸事件 */
        // 将触摸事件交给ViewDragHelper来解析处理
        viewDragHelper.processTouchEvent(event);
        super.onTouchEvent(event);
        return true;//是否继续消费后续事件。如果消费，接下来的事件还会传递到该 View 的 dispatchTouchEvent 中；如果不消费，后面的事件不会再传过来。
    }

    /**
     * 既然需要消费掉触摸事件，那么就需要拦截触摸事件
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        // 让ViewDragHelper帮我们判断是否应该拦截
        return viewDragHelper.shouldInterceptTouchEvent(ev);
    }

    /**
     * 3、实现各种处理View拖拽的回调方法
     */
    class MyCallback extends ViewDragHelper.Callback{

        /**
         * 用于判断是否捕获当前child的触摸事件
         * @param child  当前触摸的子View
         * @param pointerId ?
         * @return  true:就捕获并解析 false：不处理
         */
        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            /** 只处理 slideMain 和 slideMenu*/
            return child == slideMain || child == slideMenu;
        }

        /**
         * 当child的位置改变的时候执行,一般用来做其他子View的伴随移动(伴随动画)
         * @param changedView  位置改变的child
         * @param left child当前最新的left
         * @param top child当前最新的top
         * @param dx 本次水平移动的距离
         * @param dy 本次垂直移动的距离
         */
        @Override
        public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
            /** 计算百分比 */
            float percentage = slideMain.getLeft() / (float)dragRange;
            executeAnim(percentage);

            /** 调用接口方法 */
            if (percentage == 0f && mCurrentState == DragState.Open){
                //从开启到关闭
                listener.close();
                mCurrentState = DragState.Close;
            } else if (percentage == 1f && mCurrentState == DragState.Close){
                //从关闭到开启
                listener.open();
                mCurrentState = DragState.Open;
            } else {
                //正在关闭或者开启
                listener.onDragging(percentage);
            }
        }

        /**
         * 手指抬起的执行该方法
         * @param releasedChild 当前抬起的view
         * @param xvel x方向的移动的速度 正：向右移动， 负：向左移动
         * @param yvel y方向的移动的速度 正：向吓移动， 负：向上移动
         */
        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            /** 当slideMain滑动距离小于slideMenu的一半时，关闭，超过则打开 */
            if (slideMain.getLeft() < dragRange / 2){
                close();
            } else {
                open();
            }
        }

        /**
         * 获取view水平方向的拖拽范围,但是目前不能限制边界;
         * @param child 当前移动的view
         * @return 返回的值目前用在手指抬起的时候view缓慢移动的动画时间的计算上面，最好不要返回0（默认返回0）
         */
        @Override
        public int getViewHorizontalDragRange(View child) {
            return dragRange;
        }

        /**
         * 获取view垂直方向的拖拽范围
         * @param child 被拖拽的view
         * @return 最好不要返回0（默认返回0）
         */
        @Override
        public int getViewVerticalDragRange(View child) {
            return super.getViewVerticalDragRange(child);
        }

        /**
         * 控制child在水平方向的移动
         * @param child 被控制水平移动的view
         * @param left 表示ViewDragHelper认为你想让当前child的left改变的值,left=chile.getLeft()+dx
         * @param dx 本次child水平方向移动的距离
         * @return 表示你真正想让child的left变成的值
         */
        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            if (child == slideMain){ //只处理slideMain的水平移动
                if (left < 0){
                    left = 0;
                } else if (left > dragRange){
                    left = dragRange;
                }
            }
            if (child == slideMenu){
                left -= dx;//让slideMenu水平不动
            }
            return left;//让child跟随手指水平移动
        }

        /**
         * 控制child在垂直方向的移动
         * @param child 被控制垂直移动的view
         * @param top 表示ViewDragHelper认为你想让当前child的top改变的值,top=chile.getTop()+dy
         * @param dy 本次child垂直方向移动的距离
         * @return 表示你真正想让child的top变成的值
         */
        @Override
        public int clampViewPositionVertical(View child, int top, int dy) {
//            return top;//让child跟随手指垂直移动
            return top - dy;//让child在垂直方向上不再移动
        }


        @Override
        public void onEdgeDragStarted(int edgeFlags, int pointerId) {
            super.onEdgeDragStarted(edgeFlags, pointerId);
        }
    }

    /**
     * 执行slideMenu和slideMain动画
     */
    private void executeAnim(float percentage) {
        /** 使用ViewHelper实现View的缩放，透明，旋转等效果 */
        //slideMain的滑动动画
        ViewHelper.setScaleX(slideMain, floatEvaluator.evaluate(percentage, 1f, 0.8f));
        ViewHelper.setScaleY(slideMain, floatEvaluator.evaluate(percentage, 1f, 0.8f));

        //slideMenu的滑动动画
        ViewHelper.setTranslationX(
                slideMenu,intEvaluator.evaluate(percentage,-slideMenu.getMeasuredWidth()/2,0));
        //放大menuView
        ViewHelper.setScaleX(slideMenu,floatEvaluator.evaluate(percentage,0.5f,1f));
        ViewHelper.setScaleY(slideMenu,floatEvaluator.evaluate(percentage,0.5f,1f));
        //改变menuView的透明度
        ViewHelper.setAlpha(slideMenu,floatEvaluator.evaluate(percentage,0.3f,1f));

        //给SlideMenu的背景添加渐变黑色的遮罩效果
        getBackground().setColorFilter(
                (Integer) ColorUtil.evaluateColor(percentage, Color.BLACK, Color.TRANSPARENT),
                PorterDuff.Mode.SRC_OVER);
    }

    /**
     * 打开slideMenu
     */
    public void open() {
        /** 平滑移动  必须要刷新 */
        viewDragHelper.smoothSlideViewTo(slideMain, dragRange, slideMain.getTop());
        //触发computeScroll方法，相当于Scroller的startScroll方法一样
        ViewCompat.postInvalidateOnAnimation(SlideMenu.this);
    }

    /**
     * 关闭slideMenu
     */
    public void close() {
        viewDragHelper.smoothSlideViewTo(slideMain, 0, slideMain.getTop());
        ViewCompat.postInvalidateOnAnimation(SlideMenu.this);
    }

    /**
     * Called by a parent to request that a child update its values for mScrollX
     * and mScrollY if necessary(用于更新控件)
     */
    @Override
    public void computeScroll() {
        if(viewDragHelper.continueSettling(true)){
            ViewCompat.postInvalidateOnAnimation(SlideMenu.this);//触发指定的view一次invalidate  相当于直接调用invalidate();
//            invalidate();
//            postInvalidate(); //在不是主线程中重新绘制（刷新）ui
        }
    }

    /**
     * 定义一个接口，向外提供接口方法
     */
    public interface onDragStateListener{
        void open();
        void close();
        void onDragging(float percentage);
    }

    private onDragStateListener listener;
    /**
     * 获取接口
     */
    public void setDragStateListener(onDragStateListener listener){
        this.listener = listener;
    }
}
